<template>
  <table>
    <thead>
      <tr>
        <!-- 跨列单元格width无效 -->
        <th
          v-for="(column, index) of columns"
          :colspan="columnColspan[index]"
          :style="{ width: column.props.width }"
        >
          {{ column.props.label }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row of cellData">
        <Cell v-for="item of row" :item="item"></Cell>
      </tr>
    </tbody>
  </table>
</template>

<script>
import Vue from 'vue'

export default {
  props: {
    data: {
      type: Array,
      default: () => [] // 数据项只能为spanData.reference最下层分类
    }
  },
  data() {
    // 获取默认插槽，注意这里不再使用函数调用的形式
       const defaultSlots = this.$slots.default ? this.$slots.default() : [];
       return {
         columnColspan: defaultSlots.map(() => 1)
       }
  },
  computed: {
     columns() {
         // 同样地，这里也需要确保不将 $slots.default 作为函数调用
         return this.$slots.default ? this.$slots.default() : [];
       },
    cellData() {
      const result = Array(this.data.length)
        .fill(0)
        .map(() => [])

      for (let i = 0; i < this.data.length; i++) {
        for (let j = 0; j < this.columns.length; j++) {
          const row = this.data[i]
          const column = this.columns[j]

          if (column.children) {
            result[i].push({
              label: row[column.props.prop],
              rowspan: 1,
              colspan: 1,
              node: column.children[0].text
            })
          } else if (column.props.spanData || column.props['span-data']) {
            // 合并处理
            const spanData = column.props.spanData || column.props['span-data']

            function getParents(pid, parents = []) {
              const parent = spanData.reference.find((item) => item[spanData.id] == pid)

              if (parent) {
                parents.unshift(parent)
                getParents(parent[spanData.pid], parents)
              }

              return parents
            }

            let cellArr = getParents(row[spanData.pid]).concat([row])

            result[i].push(
              cellArr.map((item) => {
                return {
                  label: item[column.props.prop],
                  rowspan: 1,
                  colspan: 1,
                  columnIndex: j
                }
              })
            )

            const top = result[i - 1]?.[j]
            const cur = result[i][j]

            this.columnColspan[j] = Math.max(this.columnColspan[j], cur.length)

            if (!top) continue
            // 纵向数据
            const cellCol = result.filter((row) => row.length > 0).map((row) => row[j])

            for (let colIndex = 0; colIndex < this.columnColspan[j]; colIndex++) {
              for (let rowIndex = 0; rowIndex < cellCol.length; rowIndex++) {
                const cellArr = cellCol[rowIndex]

                const topCell = cellCol[rowIndex - 1]?.[colIndex]
                const curCell = cellArr[colIndex]
                // 列合并
                if (!curCell) {
                  cellArr[cellArr.length - 1].colspan = 1 + this.columnColspan[j] - cellArr.length
                }
                // 行合并
                if (topCell && topCell?.label == curCell?.label) {
                  curCell.rowspan = NaN
                  // 获取最上层的同名单元
                  function getFirstCell(rowIndex) {
                    const cell = cellCol[rowIndex - 1]?.[colIndex]
                    if (Number.isNaN(cell?.rowspan)) return getFirstCell(rowIndex - 1)
                    else return { cell, rowIndex: rowIndex - 1 }
                  }

                  const firstCell = getFirstCell(rowIndex)

                  if (firstCell.cell) {
                    firstCell.cell.rowspan = 1 + rowIndex - firstCell.rowIndex
                  }
                }
              }
            }
          } else {
            result[i].push({
              label: row[column.props.prop],
              rowspan: 1,
              colspan: 1
            })
          }
        }
      }

      return result
    }
  },
  components: {
    Cell: Vue.extend({
      props: {
        item: null
      },
      render() {
        const item = this.item

        if (Array.isArray(item)) {
          return item
            .filter((cell) => cell.rowspan)
            .map((cell) =>
              this.$createElement(
                'td',
                {
                  attrs: {
                    rowspan: cell.rowspan,
                    colspan: cell.colspan
                  },
                  style: { width: this.$parent.columns[cell.columnIndex].props.width }
                },
                cell.label
              )
            )
        } else if (item.rowspan) {
          return this.$createElement('td', { attrs: { rowspan: item.rowspan, colspan: item.colspan } }, item.node ?? item.label)
        } else {
          return null
        }
      }
    })
  }
}
</script>

<style scoped>
table {
  color: #333;
  border-collapse: collapse;
}
tr:hover {
  background-color: rgba(0, 0, 0, 0.02);
}
tr {
  /* border-bottom: 1px solid rgba(0, 0, 0, 0.05); */
}
th {
  color: #666;
  background-color: rgba(0, 0, 0, 0.02);
}
th,
:deep(td) {
  padding: 8px;
  border: 1px solid rgba(0, 0, 0, 0.05);
  text-align: center;
}
</style>